﻿using System.Reflection;
using K9Tools.Abstractions.Dependency;
using K9Tools.Abstractions.Extensions;

namespace Prism.Ioc;

public static class AssemblyRegisterExtensions
{
    public static IContainerRegistry RegisterByConvention(this IContainerRegistry container, Assembly? assembly = null)
    {
        assembly ??= Assembly.GetCallingAssembly();

        var classes = assembly.DefinedTypes
            .Where(x =>
                x is { IsClass: true, IsAbstract: false }
                && x.ImplementedInterfaces
                    .Any(i => i.Name.Equals(nameof(ISingletonDependency), StringComparison.Ordinal)
                              || i.Name.Equals(nameof(IScopedDependency), StringComparison.Ordinal)
                              || i.Name.Equals(nameof(ITransientDependency), StringComparison.Ordinal)))
            .Select(x => x.AsType());

        foreach (var implType in classes)
        {
            if (implType.IsGenericTypeDefinition)
            {
                throw new Exception($"Cannot register {implType} for conventional register does not support generic type. Please register it manually.");
            }

            var baseTypes = GetServiceTypes(implType);

            if (implType.TryGetCustomAttribute<ServiceKeyNameAttribute>(false, out var key))
            {
                Func<Type, Type, string, IContainerRegistry> registerFunc
                    = typeof(ISingletonDependency).IsAssignableFrom(implType)
                        ? container.RegisterSingleton
                        : container.Register;

                foreach (var baseType in baseTypes)
                {
                    registerFunc(baseType, implType, key!.Name.ToString());
                }
            }
            else
            {
                Func<Type, Type, IContainerRegistry> registerFunc
                    = typeof(ISingletonDependency).IsAssignableFrom(implType)
                        ? container.RegisterSingleton
                        : container.Register;

                foreach (var baseType in baseTypes)
                {
                    registerFunc(baseType, implType);
                }
            }
        }
        return container;
    }

    private static Type[] GetServiceTypes(Type implementationType)
    {
        return implementationType.GetInterfaces()
            .Where(x => implementationType.Name.EndsWith(GetConventionalName(x.Name)))
            .ToArray();
    }

    private static string GetConventionalName(string interfaceName)
    {
        return interfaceName.StartsWith('I') ? interfaceName[1..] : interfaceName;
    }
}